package com.zhan.jpa.service.impl;

import com.google.common.base.Splitter;
import com.zhan.jpa.model.BaseEntity;
import com.zhan.jpa.service.AbstractService;
import com.zhan.jpa.util.page.PageUtils;
import com.zhan.utils.Constants;
import com.zhan.utils.MyBeanUtils;
import com.zhan.utils.jwt.JwtUtils;
import com.zhan.utils.log.service.LogService;
import com.zhan.utils.model.CurrentUserInfo;
import com.zhan.utils.page.PageResult;
import com.zhan.utils.page.QueryCondition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

public abstract class AbstractServiceImpl<R extends JpaSpecificationExecutor, T extends BaseEntity> implements AbstractService<T> {

    @Autowired(required = false)
    private JwtUtils jwtUtils;

    @Autowired
    protected R repository;

    @Autowired
    protected EntityManager entityManager;

    protected PageUtils pageUtils = PageUtils.getInstance();

    @Autowired(required = false)
    protected LogService logService;

    /**
     * 公共的查询列表方法
     *
     * @param condition
     * @return
     */
    @Transactional
    @Override
    public PageResult<T> list(QueryCondition condition) {
        //查询条件
        Specification<T> spec = (Specification<T>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> list = this.addDelStatus(root, criteriaQuery, criteriaBuilder);
            if (Objects.nonNull(condition)) {
                createPredicate(root, criteriaQuery, criteriaBuilder, list, condition);
            }
            Predicate[] arr = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(arr));
        };
        return pageUtils.changeData(this.getResultPage(condition, spec));
    }

    /**
     * 按条件查询的方法
     *
     * @param root
     * @param criteriaQuery
     * @param criteriaBuilder
     * @param list
     * @param condition
     */
    protected void createPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder, List<Predicate> list, QueryCondition condition) {
    }

    /**
     * 公共的向查询条件添加分页的方法
     *
     * @param root
     * @param criteriaQuery
     * @param criteriaBuilder
     * @return
     */
    protected List<Predicate> addDelStatus(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
        List<Predicate> list = new ArrayList<>();
        // 添加删除状态
        list.add(criteriaBuilder.equal(root.get("isDelete").as(Integer.class), Constants.NOT_DEL_VALUE));
        return list;
    }

    /**
     * 公共的执行查询并分页的方法
     *
     * @param condition
     * @param spec
     * @return
     */
    protected Page<T> getResultPage(QueryCondition condition, Specification<T> spec) {
        Page<T> page = null;
        if (Objects.nonNull(condition) && condition.isPageAble()) {
            //分页的定义
            PageRequest pageable = PageRequest.of((int) condition.getPage() - 1, (int) condition.getPageSize());
            page = repository.findAll(spec, pageable);
        } else {
            List<T> all = repository.findAll(spec);
            page = new PageImpl<>(all);
        }
        if (Objects.nonNull(condition)) {
            changeListResult(condition, page);
        }
        return page;
    }

    /**
     * 公共的保存和修改方法
     *
     * @param entity
     * @return
     */
    @Transactional(rollbackOn = Exception.class)
    @Override
    public T saveOrUpdate(T entity) {
        Long id = entity.getId();
        Long userId = null;
        Date now = new Date();

        if (Objects.nonNull(jwtUtils)) {
            userId = jwtUtils.getUserId();
        }

        // 如果是新增
        if (Objects.isNull(id) || id.longValue() == 0L) {
            entity.setCreateTime(now);
            entity.setCreateUserId(userId);
            addEntity(entity);
            sendLog(entity, Constants.OperationType.INSERT, jwtUtils.getUserInfo());
        } else {
            T old = this.getById(id);
            entity.setUpdateTime(now);
            entity.setUpdateUserId(userId);
            updateEntity(old, entity);
            MyBeanUtils.copyPropertiesNotNull(entity, old);
            entity = old;
            sendLog(entity, Constants.OperationType.UPDATE, jwtUtils.getUserInfo());
        }

        return (T) getPagingAndSortingRepository().save(entity);
    }

    protected PagingAndSortingRepository getPagingAndSortingRepository() {
        PagingAndSortingRepository pagingAndSortingRepository = (PagingAndSortingRepository) repository;
        return pagingAndSortingRepository;
    }

    /**
     * 修改新增结果的钩子函数
     *
     * @param entity
     */
    protected void addEntity(T entity) {
    }

    /**
     * 修改修改结果的钩子函数
     *
     * @param oldEntity
     * @param entity
     */
    protected void updateEntity(T oldEntity, T entity) {
    }

    /**
     * 修改查询结果的钩子函数
     *
     * @param condition
     * @param page
     */
    protected void changeListResult(QueryCondition condition, Page<T> page) {
    }

    @Override
    public T getById(Long id) {
        return (T) getPagingAndSortingRepository().findById(id).get();
    }

    @Transactional(rollbackOn = Exception.class)
    @Override
    public T deleteById(Long id) throws Exception {
        T entity = this.getById(id);
        entity.setDeleteUserId(jwtUtils.getUserId());
        entity.setIsDelete(Constants.DEL_VALUE);
        entity.setDeleteTime(new Date());
        beforeDelete(entity);
        sendLog(entity, Constants.OperationType.INSERT, jwtUtils.getUserInfo());
        return (T) getPagingAndSortingRepository().save(entity);
    }

    protected void beforeDelete(T entity) throws Exception {
    }

    @Transactional(rollbackOn = Exception.class)
    @Override
    public Integer deleteByIds(String ids) throws Exception {
        List<String> list = Splitter.on(",").splitToList(ids);
        int size = list.size();
        for (int i = 0; i < size; i++) {
            String id = list.get(i);
            this.deleteById(Long.parseLong(id));
        }
        return size;
    }

    private void sendLog(Object object, Integer type, CurrentUserInfo currentUserInfo) {
        if (Objects.nonNull(logService)) {
            logService.sendLog(object, type, currentUserInfo);
        }
    }

}
